중간 코드 생성

AI
gemma-4-31b
작성자
익명
작성일
2026.07.31
조회수
7
버전
v2

📋 문서 버전

이 문서는 2개의 버전이 있습니다. 현재 최신 버전을 보고 있습니다.

중간 코드 생성

개요

중간 코드 생성( Code Generation)은 컴파일러의 핵심 단계 중 하나, 소스 코드 고수준 언어에서 하드웨어에 독립적인 중간 표현(Intermediate Representation,)으로 변환 과정입니다. 이 단계는 컴파일러의 프론트엔드(소스 언어 파싱)와 백엔드(기계어 생성)를 연결하는 다리 역할을 하며, 최적화 및 플랫폼 간 이식성을 가능하게 합니다.

중간 코드는 일반적으로 기계어보다 추상화 수준이 높지만, 소스 코드보다 구조화되어 있어 컴파일러가 분석하고 변환하기에 적합한 형태입니다. 이는 다양한 최적화 기법을 적용하기 전에 프로그램의 논리적 구조를 명확히 드러내는 데 목적이 있습니다.


중간 코드 생성의 목적

중간 코드 생성은 다음의 주요 목적을 가지고 있습니다:

  1. 플랫폼 독립성: 중간 코드는 특정 아키텍처에 종속되지 않기 때문에, 하나의 프론트엔드를 여러 백엔드에 연결할 수 있습니다.
  2. 최적화 용이성: 중간 표현은 제어 흐름, 데이터 흐름, 변수 사용 등을 분석하기 쉬운 구조를 제공하여, 다양한 컴파일러 최적화 기법을 적용할 수 있습니다.
  3. 모듈화된 설계: 컴파일러를 프론트엔드, 중간 코드 생성기, 최적화기, 백엔드로 분리함으로써 유지보수성과 확장성을 향상시킵니다.
  4. 디버깅 및 분석 지원: 중간 코드는 소스 코드에 가까우면서도 구조화되어 있어, 정적 분석 도구나 디버거 개발에 유용합니다.

중간 코드의 주요 형태

중간 코드는 여러 형태로 표현될 수 있으며, 각각의 형태는 목적과 사용 사례에 따라 적합합니다. 대표적인 형태는 다음과 같습니다:

1. 삼주소 코드 (Three-Address Code, TAC)

삼주어 코드는 하나의 연산에 최대 세 개의 주소(피연산자)를 사용하는 형태의 중간 코드입니다. 일반적인 형식은 다음과 같습니다:

x = y op z

예시:

t1 = a + b
t2 = t1 * c
d = t2 - 5

  • 장점: 구조가 단순하고 기계어로의 변환이 용이함.
  • 단점: 코드량이 늘어날 수 있음.

2. 정적 단일 할당 (Static Single Assignment, SSA)

SSA 형태는 모든 변수가 정확히 한 번만 할당되도록 변환된 중간 코드입니다. 각 변수의 이름에 버전 번호(예: x1, x2)를 붙여 데이터 흐름 분석을 용이하게 합니다.

예시:

x1 = 10
y1 = x1 + 5
x2 = φ(x1, y1)  // φ 함수는 제어 흐름 병합 시 사용

  • 장점: 데이터 흐름 분석과 최적화(예: 상수 전파, 라이브 변수 분석)에 매우 효과적.
  • 단점: 변환 과정이 복잡하며, φ 함수 처리가 필요함.

3. 추상 구문 트리 (Abstract Syntax Tree, AST)

AST는 소스 코드의 구문 구조를 트리 형태로 표현한 것으로, 중간 코드의 초기 형태로 사용됩니다. 그러나 일반적으로 더 낮은 수준의 IR로 변환되기 전까지는 최적화에 적합하지 않습니다.

  • 장점: 소스 코드와의 대응 관계가 명확함.
  • 단점: 제어 흐름 표현이 제한적.

4. 제어 흐름 그래프 (Control Flow Graph, CFG)

CFG는 프로그램의 제어 흐름을 노드(기본 블록)와 간선(분기)으로 표현한 그래프 구조입니다. 대부분의 최적화는 CFG 기반으로 수행됩니다.

  • 장점: 루프, 분기, 예외 처리 등을 명확히 표현 가능.
  • 단점: 데이터 흐름만으로는 부족하므로 IR과 함께 사용됨.

중간 코드 생성의 과정

  1. 구문 분석 후 AST 생성: 프론트엔드에서 소스 코드를 파싱하여 AST를 생성합니다.
  2. AST를 중간 코드로 변환: AST를 순회하며 삼주어 코드, SSA 형태 등으로 변환합니다.
  3. 기본 블록 생성: 순차적인 명령어를 기본 블록(unit of linear code)으로 그룹화합니다.
  4. 제어 흐름 분석: 분기문, 반복문 등을 분석하여 CFG를 구성합니다.
  5. 타입 체크 및 오류 검사: 중간 코드 수준에서 타입 일관성 및 잠재적 오류를 검사합니다.

중간 코드 생성기의 구현 예

다음은 간단한 삼주어 코드 생성기의 의사 코드입니다:

def generate_tac(node):
    if node.type == "assignment":
        temp = new_temporary()
        code = generate_tac(node.right)
        code.append(f"{temp} = {node.left.name}")
        return code
    elif node.type == "binary_op":
        left_code = generate_tac(node.left)
        right_code = generate_tac(node.right)
        temp = new_temporary()
        op = node.operator
        result_code = left_code + right_code
        result_code.append(f"{temp} = {left_result} {op} {right_result}")
        return result_code
    # 기타 노드 처리...


관련 기술 및 도구

  • LLVM IR: LLVM 프로젝트에서 사용하는 강력한 중간 표현. SSA 기반이며, 다양한 최적화와 다중 아키텍처 지원을 제공.
  • Java Bytecode: JVM에서 실행되는 중간 코드. 플랫폼 독립적이고, JIT 컴파일러에 의해 기계어로 변환됨.
  • GIMPLE: GCC에서 사용하는 SSA 기반의 중간 표현. C/C++ 소스를 단순화된 3항 연산 형태로 변환.

중간 코드의 추상화 수준

중간 표현(IR)은 소스 코드의 고수준 의미와 타겟 머신의 저수준 세부 사항 사이에서 다양한 추상화 수준을 가집니다. 현대의 복잡한 컴파일러는 단일 IR이 아닌 단계적 변환(Multi-stage IR) 전략을 채택하여 효율성을 높입니다.

고수준 IR (High-level IR)

소스 언어의 구조를 많이 유지하고 있는 형태입니다. 주로 AST(추상 구문 트리)가 이에 해당하며, 루프 구조, 고수준 데이터 타입, 함수 호출 체계 등이 명확히 드러납니다. 이 단계에서는 구문 분석 오류 수정이나 언어 특화 최적화가 수행됩니다.

저수준 IR (Low-level IR)

타겟 머신의 명령어 세트(ISA)와 유사한 형태입니다. 삼주소 코드(TAC)나 레지스터 전송 언어(RTL)가 대표적이며, 메모리 주소 계산, 레지스터 할당, 조건부 분기 등 하드웨어 동작에 가까운 표현을 사용합니다.

단계적 변환의 필요성

고수준 IR에서 곧바로 저수준 IR로 변환하면 최적화 기회를 놓치기 쉽습니다. 따라서 다음과 같은 흐름으로 점진적으로 추상화 수준을 낮춥니다: [소스 코드] $\rightarrow$ [고수준 IR (AST)] $\rightarrow$ [중간 수준 IR (SSA/TAC)] $\rightarrow$ [저수준 IR (Machine-dependent IR)] $\rightarrow$ [기계어]


IR 단계별 변환 흐름도

컴파일러 파이프라인 내에서 중간 코드가 변환되는 일반적인 흐름은 다음과 같습니다.

graph LR
    A[Source Code] --> B[AST]
    B --> C[TAC / Linear IR]
    C --> D[SSA Form]
    D --> E[CFG / Optimization]
    E --> F[Low-level IR]
    F --> G[Target Machine Code]
    
    subgraph "Front-end"
    A
    B
    end
    
    subgraph "Middle-end (Optimizer)"
    C
    D
    E
    end
    
    subgraph "Back-end"
    F
    G
    end


중간 코드 생성 시의 주요 고려사항

임시 변수 생성 전략

삼주소 코드와 같은 선형 IR을 생성할 때, 복잡한 수식을 계산하기 위해 수많은 임시 변수(Temporary Variables)가 생성됩니다. 이를 효율적으로 관리하기 위해 가상 레지스터(Virtual Register) 개념을 도입하며, 이후 백엔드 단계에서 실제 물리 레지스터로 매핑하는 레지스터 할당(Register Allocation) 과정을 거칩니다.

스택 기반 vs 레지스터 기반 표현

중간 코드를 설계할 때 피연산자를 관리하는 방식에 따라 두 가지 모델로 나뉩니다.

비교 항목 스택 기반 (Stack-based) 레지스터 기반 (Register-based)
작동 방식 푸시/팝(Push/Pop) 연산 사용 명시적인 레지스터 주소 지정
코드 크기 명령어가 짧고 콤팩트함 피연산자 명시로 인해 명령어 길이가 김
최적화 분석이 어렵고 최적화 효율이 낮음 데이터 흐름 분석 및 최적화가 매우 용이함
대표 예시 JVM Bytecode, CPython LLVM IR, Lua VM

디버그 정보(Debug Info) 유지

중간 코드로 변환되는 과정에서 소스 코드의 행 번호, 변수 이름 등의 정보가 소실됩니다. 이를 방지하기 위해 IR의 각 명령어에 소스 코드의 위치 정보를 매핑하는 메타데이터(예: DWARF 포맷)를 함께 저장하여, 최종 실행 파일에서도 디버깅이 가능하도록 설계해야 합니다.


IR 형태 간의 상호 변환 관계

중간 코드의 각 형태는 독립적으로 존재하는 것이 아니라, 컴파일 파이프라인의 목적에 따라 상호 변환됩니다.

  1. AST $\rightarrow$ TAC: 트리 구조의 재귀적 순회를 통해 선형적인 삼주소 코드로 펼칩니다(Flattening).
  2. TAC $\rightarrow$ SSA: 변수 정의-사용 체인(Def-Use Chain)을 분석하여, 변수가 중복 할당된 곳에 버전 번호를 부여하고 $\phi$ 함수를 삽입하여 SSA 형태로 변환합니다.
  3. SSA $\rightarrow$ CFG: SSA 기반의 명령어들을 기본 블록(Basic Block) 단위로 묶고, 분기문(Jump, Branch)을 간선으로 연결하여 제어 흐름 그래프를 구축합니다.

정적 분석 및 타입 추론의 역할

중간 코드 생성 단계에서의 '타입 체크'는 단순한 문법 검사를 넘어 최적화를 위한 기초 자료를 제공합니다.

  • 타입 추론(Type Inference): 명시적 타입 선언이 없는 경우, IR 수준에서 연산자의 특성을 분석해 변수의 타입을 결정합니다. 이는 메모리 할당 크기를 결정하고 적절한 기계어 명령어를 선택하는 데 필수적입니다.
  • 정적 분석(Static Analysis): SSA 형태의 IR을 활용하여 상수 전파(Constant Propagation), 죽은 코드 제거(Dead Code Elimination), 범위 분석(Range Analysis) 등을 수행합니다. 이는 런타임 오버헤드를 줄이는 결정적인 역할을 합니다.

LLVM IR 상세 분석 및 예시

LLVM IR은 현대 컴파일러 설계의 표준과도 같으며, 다음과 같은 특징을 가집니다. - Strongly Typed: 모든 값은 명확한 타입을 가져야 하며, 타입 캐스팅이 엄격합니다. - Infinite Registers: 물리적 제약이 없는 무한한 가상 레지스터를 사용하여 최적화 효율을 극대화합니다. - SSA 기반: 모든 명령어는 SSA 형태를 따릅니다.

LLVM IR 코드 예시

다음은 간단한 정수 덧셈 함수 add(a, b)의 LLVM IR 표현입니다.

; 함수 정의: i32 타입의 인자 두 개를 받아 i32를 반환
define i32 @add(i32 %a, i32 %b) {
entry:
  ; %result라는 가상 레지스터에 %a와 %b의 합을 저장 (SSA 형태)
  %result = add i32 %a, %b
  
  ; 결과값 반환
  ret i32 %result
}

이처럼 LLVM IR은 어셈블리어와 유사한 가독성을 가지면서도, 타입 정보와 SSA 구조를 유지하여 백엔드에서 다양한 CPU 아키텍처(x86, ARM, RISC-V 등)로 효율적으로 변환될 수 있습니다.

참고 자료


관련 문서

중간 코드 생성은 현대 컴파일러 기술의 핵심 요소로, 효율적이고 최적화된 프로그램 생성을 가능하게 합니다.

AI 생성 콘텐츠 안내

이 문서는 AI 모델(gemma-4-31b)에 의해 생성된 콘텐츠입니다.

주의사항: AI가 생성한 내용은 부정확하거나 편향된 정보를 포함할 수 있습니다. 중요한 결정을 내리기 전에 반드시 신뢰할 수 있는 출처를 통해 정보를 확인하시기 바랍니다.

이 AI 생성 콘텐츠가 도움이 되었나요?